Skip to content

[Version 9.0] Feature support for target typed new#1469

Draft
BillWagner wants to merge 1 commit into
draft-v9from
v9-target-typed-new
Draft

[Version 9.0] Feature support for target typed new#1469
BillWagner wants to merge 1 commit into
draft-v9from
v9-target-typed-new

Conversation

@BillWagner

@BillWagner BillWagner commented Nov 11, 2025

Copy link
Copy Markdown
Member

@RexJaeschke RexJaeschke added this to the C# 9.0 milestone Nov 12, 2025
@RexJaeschke RexJaeschke added type: feature This issue describes a new feature Review: pending Proposal is available for review labels Nov 12, 2025
@jskeet jskeet self-assigned this Nov 27, 2025

@jskeet jskeet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ready for further discussion in the group. I can then take any feedback and work on changes to the PR.

Comment thread standard/expressions.md
If any of the arguments in the optional *argument_list* has the compile-time type `dynamic` then the *object_creation_expression* is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)) and the following rules are applied at run-time using the run-time type of those arguments of the *argument_list* that have the compile-time type `dynamic`. However, the object creation undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation).

The binding-time processing of an *object_creation_expression* of the form `new T(A)`, where `T` is a *class_type*, or a *value_type*, and `A` is an optional *argument_list*, consists of the following steps:
The binding-time processing of an *object_creation_expression* of the form `new T(A)`, where the specified or implied type `T` is a *class_type*, or a *value_type*, and `A` is an optional *argument_list*, consists of the following steps:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A target-typed new expression isn't "of the form new T(A)... I think this can probably be fixed by saying something along the lines of "or a target_typed_new expression with an implied type T".

Comment thread standard/expressions.md
Even if the *object_creation_expression* is dynamically bound, the compile-time type is still `T`.

The run-time processing of an *object_creation_expression* of the form new `T(A)`, where `T` is *class_type* or a *struct_type* and `A` is an optional *argument_list*, consists of the following steps:
The run-time processing of an *object_creation_expression* of the form `new T(A)`, where the specified or implied type `T` is *class_type* or a *struct_type* and `A` is an optional *argument_list*, consists of the following steps:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another example of the same kind of thing.

Comment thread standard/conversions.md

### §imp-obj-creation-conv Implicit object-creation conversions

There is an implicit ***object-creation conversion*** from a *target_typed_new* expression ([§12.8.17.2](expressions.md#128172-object-creation-expressions)) to every type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this include pointer types?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because a pointer type can't be used in an object creation expression. See https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1281721-general

Comment thread standard/expressions.md
If any of the arguments in the optional *argument_list* has the compile-time type `dynamic` then the *object_creation_expression* is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)) and the following rules are applied at run-time using the run-time type of those arguments of the *argument_list* that have the compile-time type `dynamic`. However, the object creation undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation).

The binding-time processing of an *object_creation_expression* of the form `new T(A)`, where `T` is a *class_type*, or a *value_type*, and `A` is an optional *argument_list*, consists of the following steps:
The binding-time processing of an *object_creation_expression* of the form `new T(A)`, where the specified or implied type `T` is a *class_type*, or a *value_type*, and `A` is an optional *argument_list*, consists of the following steps:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Existing potential problem) Should this include object_or_collection_initializer?? It's sort of described earlier, but it's not really clear. We could potentially make it clear by saying that processing an expression which contains an object or collection initializer is equivalent to processing an expression without it, and then executing the object/collection initializer. Although I suspect init-only properties might make that tricky.

If it's not intended to exclude that case, why use "of the form" at all?

Comment thread standard/expressions.md

The *type* of an *object_creation_expression* shall be a *class_type*, a *value_type*, or a *type_parameter*. The *type* cannot be a *tuple_type* or an abstract or static *class_type*.

If `type` can be inferred from usage, it can be omitted, as allowed by *target_typed_new*. It is a compile-time error to omit `type` if the type cannot be inferred. A *target_typed_new* expression has no type. However, there is an implicit object-creation conversion (§imp-obj-creation-conv) from a *target_typed_new* expression to every type. It is a compile-time error if a *target_typed_new* is used as an operand of a unary or binary operator, or if it is used where it is not subject to an object-creation conversion.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If type can be inferred from usage" is too vague here.

For example, if I have:

void M(int x) { ... }
void M(Guid g) { ... }

... then can I write M(new(new byte[16])) as Guid has a constructor that accepts a byte array, but int doesn't?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I strongly suspect that clarifying this will be the biggest change required for this feature. I don't know whether we'll need to consider everywhere that target_typed_new can exist...)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one will be tough. Your example generates CS0121: The call is ambiguous between M(Guid) and M(int). That's weird, because int y = new(new byte[16])) correctly doesn't compile.

The example in the speclet shows a method with several overloads, XmlReader.Create. But, in that case, all the overloads have the same type as the 2nd positional parameter, so it's obvious what to create.

Now, if you use this: M(g: new(new byte[16]));, it compiles (note the named parameter). If you change the definition of both M methods so that the parameter name is the same, it's ambiguous again.

@BillWagner BillWagner left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial review / comments.

Comment thread standard/conversions.md

### §imp-obj-creation-conv Implicit object-creation conversions

There is an implicit ***object-creation conversion*** from a *target_typed_new* expression ([§12.8.17.2](expressions.md#128172-object-creation-expressions)) to every type.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because a pointer type can't be used in an object creation expression. See https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1281721-general

Comment thread standard/expressions.md

The *type* of an *object_creation_expression* shall be a *class_type*, a *value_type*, or a *type_parameter*. The *type* cannot be a *tuple_type* or an abstract or static *class_type*.

If `type` can be inferred from usage, it can be omitted, as allowed by *target_typed_new*. It is a compile-time error to omit `type` if the type cannot be inferred. A *target_typed_new* expression has no type. However, there is an implicit object-creation conversion (§imp-obj-creation-conv) from a *target_typed_new* expression to every type. It is a compile-time error if a *target_typed_new* is used as an operand of a unary or binary operator, or if it is used where it is not subject to an object-creation conversion.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one will be tough. Your example generates CS0121: The call is ambiguous between M(Guid) and M(int). That's weird, because int y = new(new byte[16])) correctly doesn't compile.

The example in the speclet shows a method with several overloads, XmlReader.Create. But, in that case, all the overloads have the same type as the 2nd positional parameter, so it's obvious what to create.

Now, if you use this: M(g: new(new byte[16]));, it compiles (note the named parameter). If you change the definition of both M methods so that the parameter name is the same, it's ambiguous again.

@BillWagner
BillWagner force-pushed the v9-target-typed-new branch from 0c484ac to 37f1f97 Compare April 10, 2026 20:04
@BillWagner
BillWagner force-pushed the v9-target-typed-new branch from 37f1f97 to 3b403b9 Compare May 12, 2026 21:59
Add support for target-typed new expressions

Add support for target-typed new expressions

fix link

fix link

fix warnings
@BillWagner
BillWagner force-pushed the v9-target-typed-new branch from 3b403b9 to a579c27 Compare June 24, 2026 17:39
@BillWagner

Copy link
Copy Markdown
Member Author

An earlier version of this feature is already present on alpha-v9 from a prior meeting. Edits made to this PR since then are not yet on alpha-v9; they will land at the next propagation. If you need them on alpha-v9 sooner, please open a separate PR targeting alpha-v9.
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review: pending Proposal is available for review type: feature This issue describes a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants